package com.litaomn.fang.hotload.util;

import android.annotation.SuppressLint;
import android.app.Application;
import android.util.Log;

import com.alibaba.fastjson.JSON;
import com.litaomn.fang.hotload.SingletonXC;
import com.tencent.mm.fang.FangBridge;
import com.tencent.mm.fang.FangHelpers;
import com.tencent.mm.fang.XC_MethodHook;

import java.lang.reflect.Method;


/**
 * Created by virjar on 2017/12/23.
 * <br>处理XposedHelpers一些不能处理的问题
 */

public class FangReflectUtil {

    //能够子子类向父类寻找method
    public static void findAndHookMethodWithSupperClass(Class<?> clazz, String methodName,
                                                        Object... parameterTypesAndCallback) {
        Class theClazz = clazz;
        do {
            try {
                FangHelpers.fhm(theClazz, methodName, parameterTypesAndCallback);
                return;
            } catch (NoSuchMethodError e) {
                //ignore
            }
        } while ((theClazz = theClazz.getSuperclass()) != null);
        throw new NoSuchMethodError("no method " + methodName + " for class:" + clazz.getName());
    }


    /**
     * 很多时候只有一个名字，各种寻找参数类型太麻烦了,自带到find supper class的工作，请注意XposedBridge#hookAllMethods(java.lang
     * .Class, java.lang.String, XC_MethodHook)并不会call supper
     *
     * @param clazz        class对象
     * @param methodName   想要被hook的方法名字
     * @param xcMethodHook 回调函数
     */
    public static void findAndHookOneMethod(Class<?> clazz, String methodName,
                                            XC_MethodHook xcMethodHook) {
        Class theClazz = clazz;

        do {
            Method[] declaredMethods = theClazz.getDeclaredMethods();
            for (Method method : declaredMethods) {
                if (method.getName().equals(methodName)) {
                    FangBridge.hookMethod(method, xcMethodHook);
                    return;
                }
            }
        } while ((theClazz = theClazz.getSuperclass()) != null);
        throw new NoSuchMethodError("no method " + methodName + " for class:" + clazz.getName());
    }


    public static void printAllMethod(Class clazz) {
        while (clazz != null) {
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for (Method method : declaredMethods) {
                FangBridge.log("printMethod: " + method);
                Log.i("weijia", "printMethod: " + method);
            }
            clazz = clazz.getSuperclass();
        }
    }

    public static void monitorMethodsByReturn(Class clazz, String returnClass) {
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method method : declaredMethods) {
            if (method.getReturnType().getSimpleName().equals(returnClass)) {
                FangBridge.hookMethod(method, new XC_MethodHook() {
                    @Override
                    protected void am(MethodHookParam param) throws Throwable {
                        String result = (String) param.getResult();
                        LogUtil.error(result);
                    }
                });
            }
        }
    }

    /**
     * 基于堆栈回溯方法，可以找方法名字，但是存在方法重载的时候，一个methodName可能对应多个实现。此时由于行号信息不应正常，这会导致无法确定具体那个方法被调用。
     * <br>
     * 通过此方法打印所有同名函数，帮组确定那个方法被调用
     *
     * @param clazz class
     * @param name  希望被监控的方法名称
     */
    public static void monitorMethodCall(Class clazz, String name) {
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method method : declaredMethods) {
            if (method.getName().equals(name)) {
                FangBridge.hookMethod(method, methodCallPrintHook);
            }
        }
    }

    public static void monitorAllMethodCall(Class clazz) {
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method method : declaredMethods) {
            FangBridge.hookMethod(method, methodCallPrintHook);
        }
    }

    private static SingletonXC methodCallPrintHook = new SingletonXC() {
        @Override
        protected void bm(XC_MethodHook.MethodHookParam param) throws Throwable {
            //Log.e("methodCall", "the method: " + param.method);
            LogUtil.error("methodCall: the mothod:" + param.method);
            LogUtil.error("methodCall: " + JSON.toJSONString(ForceFiledViewer.toView(param.args)));
        }
    };

    @SuppressLint("PrivateApi")
    public static Application getApplicationUsingReflection() throws Exception {
        return (Application) Class.forName("android.app.ActivityThread").getMethod(
                "currentApplication").invoke(null);
    }


}